import numpy as np
class SGD:
    def __init__(self,lr=0.1):
        self.lr=lr
    def update(self,params,grads):
        for key in params.keys():
            params[key]-=self.lr*grads[key]
#想象成物理
class Momentum:
    def __init__(self,lr=0.01,momentum=0.9):
        self.lr=lr #学习率（步长），控制每次更新的幅度
        self.momentum=momentum #动量系数，控制历史速度的保留程度
        self.v=None#保存当前速度
    def update(self,params,grads):
        if self.v is None:#初始速度还没有
            self.v={} #创建空字典存储每个参数的动量（例如 w1，b1，w2，b2）
            for key,val in params.items():
                #第一次调用update的时候，self.v是 None，需要为每个参数创建一个和它形状
                #相同的‘动量项’，初始为 0
                self.v[key]=np.zeros_like(val)
        for key in params.keys():
            #计算当前动量项：历史动量的衰减（momentum*上一次动量）+当前梯度的贡献（-lr*梯度）
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
            #用动量项更新参数：参数=原参数+当前动量项（等价于沿动量方向移动）
            params[key]+=self.v[key]
#学习率衰减
class AdaGrad:
    def __init__(self,lr=0.01):
        self.lr=lr
        self.h=None#保存当前梯度值的平方和

    def update(self,params,grads):
        #第一次调用update的时候，self.h里面还没有值，所以要根据每个参数的形状新建对应形状的全为 0 的数组
        if self.h is None:
            self.h={}
            for key,val in params.items():
                self.h[key]=np.zeros_like(val)
        for key in params.keys():
            self.h[key]+=grads[key]*grads[key]#这是累计参数的“历史梯度平方和”
            params[key]-=self.lr*grads[key]/(np.sqrt(self.h[key])+1e-7)#(np.sqrt(self.h[key])+1e-7)这个是对那个历史梯度平方和开根号，
            # 然后加极小值避免分母为 0
#我在逐渐遗忘过去的所有梯度
class RMSProp:
    def __init__(self,lr=0.01,decay_rate=0.99):
        self.lr=lr
        self.decay_rate=decay_rate#衰退系数
        self.h=None
    def update(self,params,grads):
        if self.h is None:
            self.h={}
            for key,val in params.items():
                self.h[key]=np.zeros_like(val)
        for key in params.keys():
            self.h[key]*=self.decay_rate#每次乘了衰退系数，让他的大小呈现指数级别的降低
            self.h[key]+=(1-self.decay_rate)*grads[key]*grads[key]#对新的梯度更新用加法来的
            params[key]-=self.lr*grads[key]/(np.sqrt(self.h[key])+1e-7)
class Adam:
    def __init__(self,lr=0.01,beta1=0.9,beta2=0.99):
        self.lr=lr#初始学习率
        self.beta1=beta1#一阶动量衰减系数
        self.beta2=beta2#二阶动量衰减系数
        self.iter=0
        self.m=None
        self.v=None
    def update(self,params,grads):
        if self.m is None:
            self.m,self.v={},{}
            for key,val in params.items():
                self.m[key]=np.zeros_like(val)#一阶动量,动量法的速度
                self.v[key]=np.zeros_like(val)#二阶动量，梯度平方积累
        self.iter+=1#记录当前是第几次更新
        lr_t=self.lr * np.sqrt(1-self.beta2**self.iter)/(1-self.beta1**self.iter)#修正后的学习率
        for key in params.keys():
            self.m[key]+=(1-self.beta1)*(grads[key]-self.m[key])
            self.v[key]+=(1-self.beta2)*(grads[key]**2-self.v[key])
            params[key]-=lr_t*self.m[key] / (np.sqrt(self.v[key])+1e-7)
